home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 1 / QRZ Ham Radio Callsign Database - December 1993.iso / ucsd / packet / tcpip / sys5 / iscwmpst.z / iscwmpst / tcp / isc-src / util / misc / xlogin.c < prev   
Encoding:
C/C++ Source or Header  |  1991-10-29  |  21.2 KB  |  936 lines

  1. /*    @(#)login.c    2.1        */
  2. /*
  3.  * login [ name ] [ environment args ]
  4.  *
  5.  *    Conditional assemblies:
  6.  *
  7.  *    PASSREQ    causes password to be required
  8.  *    NO_MAIL    causes the MAIL environment variable not to be set
  9.  *    NOSHELL does not put non-standard shell names into environment
  10.  *    CONSOLE, if defined, only allows root logins on the device
  11.  *        specified by CONSOLE.  CONSOLE MUST NOT be defined as
  12.  *        either "/dev/syscon" or "/dev/systty"!!
  13.  */
  14.  
  15. #include <sys/types.h>
  16. #include <utmp.h>
  17. #include <signal.h>
  18. #include <pwd.h>
  19. #include <stdio.h>
  20. #include <shadow.h>
  21. #include <sys/stat.h>
  22. #include <sys/dir.h>
  23.  
  24. #define    TRUE        1
  25. #define    FALSE        0
  26. #define    FAILURE        -1
  27.  
  28. #define SCPYN(a, b)    strncpy(a, b, sizeof(a))
  29. #define DIAL_FILE    "/etc/dialups"
  30. #define DPASS_FILE    "/etc/d_passwd"
  31. #define SHELL        "/bin/sh"
  32. #define    PATH        "PATH=:/bin:/usr/bin:/usr/local/bin"
  33. #define    ROOTPATH    "PATH=:/bin:/usr/bin:/etc"
  34. #define SUBLOGIN    "<!sublogin>"
  35.  
  36. #define    ROOTUID        0
  37.  
  38. #define    MAXARGS        63
  39. #define    MAXENV        1024
  40. #define MAXLINE        256
  41.  
  42. struct    passwd nouser = {"", "nope"};
  43. struct    utmp utmp;
  44. char    u_name[64];
  45. char    minusnam[16] = "-";
  46. char    shell[64] = { "SHELL=" };
  47. char    home[64] = { "HOME=" };
  48. char    logname[30] = { "LOGNAME=" };
  49.  
  50. #ifndef    NO_MAIL
  51. char    mail[30] = { "MAIL=/usr/mail/" };
  52. #endif
  53.  
  54. char    *envinit[5+MAXARGS] = {
  55.         home,PATH,logname,0,0
  56.     };
  57. int    basicenv;
  58. char    envblk[MAXENV];
  59. struct    passwd *pwd;
  60.  
  61. struct  spwd *spwd;
  62.  
  63. struct    passwd *getpwnam();
  64. /* int    setpwent(); */
  65. char    *crypt();
  66. char    *getpass();
  67. char    *strrchr(),*strchr(),*strcat();
  68. extern    char **environ;
  69.  
  70. #define    WEEK    (24L * 7 * 60 * 60) /* 1 week in seconds */
  71. time_t    when;
  72. time_t    maxweeks;
  73. time_t    minweeks;
  74. time_t    now;
  75. long     a64l(), time();
  76.  
  77. char *truename = NULL;
  78.  
  79. main(argc, argv ,renvp)
  80. char **argv,**renvp;
  81. {
  82.     register char *namep;
  83.     int j,k,l_index,length;
  84.     char *ttyn,*ttyntail;
  85.     int nopassword;
  86.     register int i;
  87.     register struct utmp *u;
  88.     struct utmp *getutent(), *pututline();
  89.     FILE *fp;
  90.     char **envp,*ptr,*endptr;
  91.     int sublogin;
  92.     extern char **getargs();
  93.     extern char *terminal();
  94.  
  95. /* special stuff, check for 'TRUENAME=' as first! parameter */
  96.     if (!strncmp (argv [1], "TRUENAME=", 9)) {
  97.         truename = argv[1] + 9;
  98.         argv++;
  99.         argc--;
  100.     }
  101.  
  102. /*    Set flag to disable the pid check if you find that you are    */
  103. /*    a subsystem login.                        */
  104.  
  105.     sublogin = FALSE;
  106.     if (*renvp && strcmp(*renvp,SUBLOGIN) == 0)
  107.         sublogin = TRUE;
  108.  
  109.     umask(0);
  110.     alarm(120);
  111.     signal(SIGQUIT, 1);
  112.     signal(SIGINT, 1);
  113.     nice(0);
  114.     ttyn = terminal(0);
  115.     if (ttyn==0)
  116.         ttyn = "/dev/tty??";
  117.     if (ttyntail = strrchr(ttyn,'/')) ttyntail++;
  118.     else ttyntail = ttyn;
  119.  
  120.     if (argc > 1) {
  121.         if (truename)
  122.             SCPYN (utmp.ut_user, truename);
  123.         else
  124.             SCPYN(utmp.ut_user, argv[1]);
  125.         strncpy(u_name, argv[1], 64);
  126.         envp = &argv[2];
  127.         goto first;
  128.     }
  129.  
  130. loop:
  131.     u_name[0] = utmp.ut_user[0] = '\0';
  132. first:
  133.     while (utmp.ut_user[0] == '\0') {
  134.         printf("login: ");
  135.         if ((envp = getargs()) != (char**)NULL) {
  136.             SCPYN(utmp.ut_user,*envp);
  137.             strncpy(u_name, *envp++, 64);
  138.         }
  139.     }
  140.     setpwent();
  141.     if ((pwd = getpwnam(u_name)) == NULL)
  142.         pwd = &nouser;
  143.     endpwent();
  144. /* OLE */
  145.         setspent();
  146.     if ((spwd = getspnam(u_name)) == NULL) 
  147.          spwd = &nouser;
  148.     endspent();    
  149.     pwd->pw_passwd = spwd->sp_pwdp;
  150. /* OLE */
  151.     if (strncmp("pri=", pwd->pw_gecos, 4) == 0) {
  152.         int    mflg, pri;
  153.  
  154.         pri = 0;
  155.         mflg = 0;
  156.         i = 4;
  157.         if (pwd->pw_gecos[i] == '-') {
  158.             mflg++;
  159.             i++;
  160.         }
  161.         while(pwd->pw_gecos[i] >= '0' && pwd->pw_gecos[i] <= '9')
  162.             pri = (pri * 10) + pwd->pw_gecos[i++] - '0';
  163.         if (mflg)
  164.             pri = -pri;
  165.         nice(pri);
  166.     }
  167.     nopassword = 1;
  168.     if (*pwd->pw_passwd != '\0') {
  169.         nopassword = 0;
  170.         if(gpass("Password:", pwd->pw_passwd))
  171.             goto loop;
  172.     }
  173.  
  174.     /*
  175.      * get dialup password, if necessary
  176.      */
  177.     if(dialpass(ttyn))
  178.         goto loop;
  179.  
  180.     if(chdir(pwd->pw_dir) < 0) {
  181.         printf("Unable to change directory to \"%s\"\n",pwd->pw_dir);
  182.         if(chdir("/") < 0) {
  183.             printf("Unable to change directory to \"/\"\n");
  184.             goto loop;
  185.         }
  186.         printf("You are in \"/\" instead.\n");
  187.     }
  188.     time(&utmp.ut_time);
  189.     utmp.ut_pid = getpid();
  190.  
  191. /*    Find the entry for this pid (or line if we are a sublogin) in    */
  192. /*    the utmp file.                            */
  193.  
  194.     while ((u = getutent()) != NULL) {
  195.         if ((u->ut_type == INIT_PROCESS || u->ut_type ==
  196.             LOGIN_PROCESS || u->ut_type == USER_PROCESS)
  197.             && ( (sublogin && strncmp(u->ut_line,ttyntail,
  198.             sizeof(u->ut_line)) == 0) || u->ut_pid == utmp.ut_pid) ) {
  199.  
  200. /*    Copy in the name of the tty minus the "/dev/", the id, and set    */
  201. /*    the type of entry to USER_PROCESS.                */
  202.  
  203.             SCPYN(utmp.ut_line, ttyntail);
  204.             utmp.ut_id[0] = u->ut_id[0];
  205.             utmp.ut_id[1] = u->ut_id[1];
  206.             utmp.ut_id[2] = u->ut_id[2];
  207.             utmp.ut_id[3] = u->ut_id[3];
  208.             utmp.ut_type = USER_PROCESS;
  209.  
  210. /*    Return the new updated utmp file entry.                */
  211.  
  212.             pututline(&utmp);
  213.             break;
  214.         }
  215.     }
  216.     endutent();        /* Close utmp file */
  217.  
  218.     if (u == (struct utmp *)NULL){
  219.         printf("No utmp entry.  You must exec \"login\" from\
  220.  the lowest level \"sh\".\n");
  221.         exit(1);
  222.     }
  223.  
  224. /*    Now attempt to write out this entry to the wtmp file if we    */
  225. /*    were successful in getting it from the utmp file and the    */
  226. /*    wtmp file exists.                        */
  227.  
  228.     if (u != NULL && (fp = fopen(WTMP_FILE,"r+")) != NULL) {
  229.         fseek(fp,0L,2);        /* Seek to end of file. */
  230.         fwrite(&utmp,sizeof(utmp),1,fp);
  231.         fclose(fp);
  232.     }
  233.     chown(ttyn, pwd->pw_uid, pwd->pw_gid);
  234.  
  235. /*    If the shell field starts with a '*', do a chroot to the home    */
  236. /*    directory and perform a new login.                */
  237.  
  238.     if(*pwd->pw_shell == '*') {
  239.         if(chroot(pwd->pw_dir) < 0) {
  240.             printf("No Root Directory\n");
  241.             goto loop;
  242.         }
  243.  
  244. /*    Set the environment flag <!sublogin> so that the next login    */
  245. /*    knows that it is a sublogin.                    */
  246.  
  247.         envinit[0] = SUBLOGIN;
  248.         envinit[1] = (char*)NULL;
  249.         printf("Subsystem root: %s\n",pwd->pw_dir);
  250.         execle("/bin/login", "login", 0,&envinit[0]);
  251.         execle("/etc/login", "login", 0,&envinit[0]);
  252.         printf("No /bin/login or /etc/login on root\n");
  253.         goto loop;
  254.     }
  255.  
  256.     if (setgid(pwd->pw_gid) == FAILURE) {
  257.         printf("Bad group id.\n");
  258.         exit(1);
  259.     }
  260.     if (setuid(pwd->pw_uid) == FAILURE) {
  261.         printf("Bad user id.\n");
  262.         exit(1);
  263.     }
  264.  
  265. /*    Set up the basic environment for the exec.  This includes    */
  266. /*    HOME, PATH, LOGNAME, SHELL, and MAIL.                */
  267.  
  268.     strcat(home, pwd->pw_dir);
  269.     if (truename)
  270.         strcat (logname, truename);
  271.     else
  272.         strcat(logname, pwd->pw_name);
  273.     if(pwd->pw_uid == ROOTUID) {
  274. #ifdef CONSOLE
  275.         if(strcmp(ttyn, CONSOLE)) {
  276.             printf("Not on system console\n");
  277.             exit(10);
  278.         }
  279. #endif
  280.         envinit[1] = ROOTPATH;
  281.     }
  282.     if (*pwd->pw_shell == '\0') {
  283.         pwd->pw_shell = SHELL;
  284.         strcat(shell, pwd->pw_shell);
  285.     }
  286. #ifndef NOSHELL
  287.     else {
  288.         envinit[3] = shell;
  289.         strcat(shell, pwd->pw_shell);
  290.     }
  291. #endif
  292.  
  293. /*    Find the name of the shell.                    */
  294.  
  295.     if ((namep = strrchr(pwd->pw_shell, '/')) == NULL)
  296.         namep = pwd->pw_shell;
  297.     else
  298.         namep++;
  299.  
  300. /*    Generate the name of the shell with a '-' sign in front of it.    */
  301. /*    This causes .profile processing when a shell is exec'ed.    */
  302.  
  303.     strcat(minusnam, namep);
  304.  
  305. #ifndef    NO_MAIL
  306.     if (envinit[3] == (char*)NULL) envinit[3] = mail;
  307.     else envinit[4] = mail;
  308.     if (truename)
  309.         strcat (mail, truename);
  310.     else
  311.         strcat(mail,pwd->pw_name);
  312. #endif
  313.  
  314. /*    Find the end of the basic environment.                */
  315.  
  316.     for (basicenv=3; envinit[basicenv];basicenv++);
  317.  
  318. /*    Add in all the environment variables picked up from the        */
  319. /*    argument list to "login" or from the user response to the    */
  320. /*    "login" request.                        */
  321.  
  322.     for (j=0,k=0,l_index=0,ptr= &envblk[0]; *envp && j < MAXARGS-1
  323.         ;j++,envp++) {
  324.  
  325. /*    Scan each string provided.  If it doesn't have the format    */
  326. /*    xxx=yyy, then add the string "Ln=" to the beginning.        */
  327.  
  328.         if ((endptr = strchr(*envp,'=')) == (char*)NULL) {
  329.             envinit[basicenv+k] = ptr;
  330.             sprintf(ptr,"L%d=%s",l_index,*envp);
  331.  
  332. /*    Advance "ptr" to the beginning of the next argument.        */
  333.  
  334.             while (*ptr++);
  335.             k++;
  336.             l_index++;
  337.         } else {
  338.  
  339. /*    Check to see whether this string replaces any previously    */
  340. /*    defined string.                            */
  341.  
  342.             for (i=0,length= endptr+1-*envp; i < basicenv+k;i++) {
  343.                 if (strncmp(*envp,envinit[i],length) == 0) {
  344.  
  345. /*    If the variable to be changed is "SHELL=" or "PATH=", don't    */
  346. /*    allow the substitution.                        */
  347.  
  348.                     if (strncmp(*envp,"SHELL=",6) != 0
  349.                         && strncmp(*envp,"PATH=",5) != 0)
  350.                         envinit[i] = *envp;
  351.                     break;
  352.                 }
  353.             }
  354.  
  355. /*    If it doesn't, place it at the end of environment array.    */
  356.  
  357.             if (i == basicenv+k) {
  358.                 envinit[basicenv+k] = *envp;
  359.                 k++;
  360.             }
  361.         }
  362.     }
  363.  
  364. /*    Switch to the new environment.                    */
  365.  
  366.     environ = envinit;
  367.     alarm(0);
  368. #ifdef PASSREQ
  369.     if (nopassword) {
  370.         printf("You don't have a password.  Choose one.\n");
  371.         printf("passwd %s\n", u_name);
  372.         execl("/bin/passwd", "passwd", u_name, 0);
  373.         printf("Can not execute /bin/passwd\n");
  374.         exit(1);
  375.     }
  376. #endif
  377.  
  378. /* is the age of the password to be checked? */
  379.  
  380.     if (*pwd->pw_age != NULL) {
  381.         /* retrieve (a) week of previous change; 
  382.             (b) maximum number of valid weeks    */
  383.         when = (long) a64l (pwd->pw_age);
  384.         /* max, min and weeks since last change are packed radix 64 */
  385.         maxweeks = when & 077;
  386.         minweeks = (when >> 6) & 077;
  387.         when >>= 12;
  388.         now  = time(0)/WEEK;
  389.         if (when > now || (now > when + maxweeks) && (maxweeks >= minweeks)) {
  390.             printf ("Your password has expired. Choose\
  391.  a new one\n");
  392.             execl("/bin/passwd", "passwd", u_name, 0);
  393.             exit(9);
  394.         }
  395.     }
  396.  
  397.     signal(SIGQUIT, 0);
  398.     signal(SIGINT, 0);
  399.     execlp(pwd->pw_shell, minusnam, 0);
  400.     printf("No shell\n");
  401.     exit(1);
  402. }
  403.  
  404. dialpass(ttyn)
  405. char *ttyn;
  406. {
  407.     register FILE *fp;
  408.     char defpass[30];
  409.     char line[80];
  410.     register char *p1, *p2;
  411.  
  412.     if((fp=fopen(DIAL_FILE, "r")) == NULL)
  413.         return(0);
  414.     while((p1 = fgets(line, sizeof(line), fp)) != NULL) {
  415.         while(*p1 != '\n' && *p1 != ' ' && *p1 != '\t')
  416.             p1++;
  417.         *p1 = '\0';
  418.         if(strcmp(line, ttyn) == 0)
  419.             break;
  420.     }
  421.     fclose(fp);
  422.     if(p1 == NULL || (fp = fopen(DPASS_FILE, "r")) == NULL)
  423.         return(0);
  424.     while((p1 = fgets(line, sizeof(line)-1, fp)) != NULL) {
  425.         while(*p1 && *p1 != ':')
  426.             p1++;
  427.         *p1++ = '\0';
  428.         p2 = p1;
  429.         while(*p1 && *p1 != ':')
  430.             p1++;
  431.         *p1 = '\0';
  432.         if(strcmp(pwd->pw_shell, line) == 0) {
  433.             fclose(fp);
  434.             if (*p2 == '\0')
  435.                 return(0);
  436.             return(gpass("Dialup Password:", p2));
  437.         }
  438.         if(strcmp(SHELL, line) == 0)
  439.             SCPYN(defpass, p2);
  440.     }
  441.     fclose(fp);
  442.     return(gpass("Dialup Password:", defpass));
  443. }
  444.  
  445. gpass(prmt, pswd)
  446. char *prmt, *pswd;
  447. {
  448.     register char *p1;
  449.  
  450.     p1 = crypt(getpass(prmt), pswd);
  451.     if(strcmp(p1, pswd)) {
  452.         printf("Login incorrect\n");
  453.         return(1);
  454.     }
  455.     return(0);
  456. }
  457.  
  458. #define    WHITESPACE    0
  459. #define    ARGUMENT    1
  460.  
  461. char **getargs()
  462. {
  463.     static char envbuf[MAXLINE];
  464.     static char *args[MAXARGS];
  465.     register char *ptr,**answer;
  466.     register int c;
  467.     int state;
  468.     extern int quotec();
  469.  
  470.     for (ptr= &envbuf[0]; ptr < &envbuf[sizeof(envbuf)];) *ptr++ = '\0';
  471.     for (answer= &args[0]; answer < &args[MAXARGS];)
  472.         *answer++ = (char *)NULL;
  473.     for (ptr= &envbuf[0],answer= &args[0],state = WHITESPACE;
  474.          (c = getc(stdin)) != EOF;) {
  475.  
  476.         switch (c) {
  477.         case '\n' :
  478.             if (ptr == &envbuf[0]) return((char **)NULL);
  479.             else return(&args[0]);
  480.         case ' ' :
  481.         case '\t' :
  482.             if (state == ARGUMENT) {
  483.                 *ptr++ = '\0';
  484.                 state = WHITESPACE;
  485.             }
  486.             break;
  487.         case '\\' :
  488.             c = quotec();
  489.         default :
  490.             if (state == WHITESPACE) {
  491.                 *answer++ = ptr;
  492.                 state = ARGUMENT;
  493.             }
  494.             *ptr++ = c;
  495.         }
  496.  
  497. /*    If the buffer is full, force the next character to be read to    */
  498. /*    be a <newline>.                            */
  499.  
  500.         if (ptr == &envbuf[sizeof(envbuf)-1]) {
  501.             ungetc('\n',stdin);
  502.             putc('\n',stdout);
  503.         }
  504.     }
  505.  
  506. /*    If we left loop because an EOF was received, exit immediately.    */
  507.  
  508.     exit(0);
  509. }
  510.  
  511. int quotec()
  512. {
  513.     register int c,i,num;
  514.  
  515.     switch(c = getc(stdin)) {
  516.     case 'n' :
  517.         c = '\n';
  518.         break;
  519.     case 'r' :
  520.         c = '\r';
  521.         break;
  522.     case 'v' :
  523.         c = 013;
  524.         break;
  525.     case 'b' :
  526.         c = '\b';
  527.         break;
  528.     case 't' :
  529.         c = '\t';
  530.         break;
  531.     case 'f' :
  532.         c = '\f';
  533.         break;
  534.     case '0' :
  535.     case '1' :
  536.     case '2' :
  537.     case '3' :
  538.     case '4' :
  539.     case '5' :
  540.     case '6' :
  541.     case '7' :
  542.         for (num=0,i=0; i < 3;i++) {
  543.             num = num * 8 + (c - '0');
  544.             if ((c = getc(stdin)) < '0' || c > '7')
  545.                 break;
  546.         }
  547.         ungetc(c,stdin);
  548.         c = num & 0377;
  549.         break;
  550.     default :
  551.         break;
  552.     }
  553.     return (c);
  554. }
  555. /*
  556.  * termainal(f): return "/dev/ttyXX" which the the name of the
  557.  * tty belonging to file f.  This routine is the same as ttyname()
  558.  * except that it rejects /dev/syscon and /dev/systty, which are
  559.  * links to other device names.
  560.  *
  561.  * This program works in two passes: the first pass tries to
  562.  * find the device by matching device and inode numbers; if
  563.  * that doesn't work, it tries a second time, this time doing a
  564.  * stat on every file in /dev and trying to match device numbers
  565.  * only. If that fails too, NULL is returned.
  566.  */
  567.  
  568. char *
  569. terminal(f)
  570. int    f;
  571. {
  572.     struct stat fsb, tsb;
  573.     struct direct db;
  574.     register int df, pass1;
  575.     extern char *strcpy(), *strcat();
  576.     extern long lseek();
  577.     static char rbuf[32], dev[]="/dev/";
  578.  
  579.     if(isatty(f) == 0) {
  580.         return(NULL);
  581.     }    
  582.     if(fstat(f, &fsb) < 0) {
  583.         return(NULL);
  584.     }    
  585.     if((fsb.st_mode & S_IFMT) != S_IFCHR) {
  586.         return(NULL);
  587.     }    
  588.     if((df = open(dev, 0)) < 0) {
  589.         return(NULL);
  590.     }
  591.     pass1 = 1;
  592.     do {
  593.         while(read(df, (char *)&db, sizeof(db)) == sizeof(db)) {
  594.             if(db.d_ino == 0)
  595.                 continue;
  596.             if(pass1 && db.d_ino != fsb.st_ino)
  597.                 continue;
  598.             if (strncmp(&db.d_name[0],"syscon",6) == 0 ||
  599.                 strncmp(&db.d_name[0],"systty",6) == 0) {
  600.                 continue;
  601.             }
  602.             (void) strcpy(rbuf, dev);
  603.             (void) strcat(rbuf, db.d_name);
  604.             if(stat(rbuf, &tsb) < 0) {
  605.                 continue;
  606.             }    
  607.             if(tsb.st_rdev == fsb.st_rdev &&
  608.                 (tsb.st_mode&S_IFMT) == S_IFCHR &&
  609.                 (!pass1 || tsb.st_ino == fsb.st_ino)) {
  610.                 (void) close(df);
  611.                 return(rbuf);
  612.             }
  613.         }
  614.         (void) lseek(df, 0L, 0);
  615.     } while(pass1--);
  616.     (void) close(df);
  617.     return(NULL);
  618. }
  619.  
  620. /*    @(#)getut.c    1.1    */
  621.  
  622. /*    Routines to read and write the /etc/utmp file.            */
  623. /*                                    */
  624. #include    <sys/param.h>
  625. #include    <errno.h>
  626. #include    <fcntl.h>
  627.  
  628. #define    MAXFILE    79    /* Maximum pathname length for "utmp" file */
  629.  
  630. #ifdef    DEBUG
  631. #undef    UTMP_FILE
  632. #define    UTMP_FILE "utmp"
  633. #endif
  634.  
  635. static int fd = -1;    /* File descriptor for the utmp file. */
  636. static char utmpfile[MAXFILE+1] = UTMP_FILE;    /* Name of the current
  637.                          * "utmp" like file.
  638.                          */
  639. static long loc_utmp;    /* Where in "utmp" the current "ubuf" was
  640.              * found.
  641.              */
  642. static struct utmp ubuf;    /* Copy of last entry read in. */
  643.  
  644.  
  645. /* "getutent" gets the next entry in the utmp file.
  646.  */
  647.  
  648. struct utmp *getutent()
  649. {
  650.     extern int fd;
  651.     extern char utmpfile[];
  652.     extern struct utmp ubuf;
  653.     extern long loc_utmp,lseek();
  654.     extern int errno;
  655.     register char *u;
  656.     register int i;
  657.     struct stat stbuf;
  658.  
  659. /* If the "utmp" file is not open, attempt to open it for
  660.  * reading.  If there is no file, attempt to create one.  If
  661.  * both attempts fail, return NULL.  If the file exists, but
  662.  * isn't readable and writeable, do not attempt to create.
  663.  */
  664.  
  665.     if (fd < 0) {
  666.  
  667. /* Make sure file is a multiple of 'utmp' entries long */
  668.         if (stat(utmpfile,&stbuf) == 0) {
  669.             if((stbuf.st_size % sizeof(struct utmp)) != 0) {
  670.                 unlink(utmpfile);
  671.             }
  672.         }
  673.         if ((fd = open(utmpfile, O_RDWR|O_CREAT, 0644)) < 0) {
  674.  
  675. /* If the open failed for permissions, try opening it only for
  676.  * reading.  All "pututline()" later will fail the writes.
  677.  */
  678.             if (errno == EACCES
  679.                 && (fd = open(utmpfile, O_RDONLY)) < 0)
  680.                 return(NULL);
  681.         }
  682.     }
  683.  
  684. /* Try to read in the next entry from the utmp file.  */
  685.     if (read(fd,&ubuf,sizeof(ubuf)) != sizeof(ubuf)) {
  686.  
  687. /* Make sure ubuf is zeroed. */
  688.         for (i=0,u=(char *)(&ubuf); i<sizeof(ubuf); i++) *u++ = '\0';
  689.         loc_utmp = 0;
  690.         return(NULL);
  691.     }
  692.  
  693. /* Save the location in the file where this entry was found. */
  694.     loc_utmp = lseek(fd,0L,1) - (long)(sizeof(struct utmp));
  695.     return(&ubuf);
  696. }
  697.  
  698. /*    "getutid" finds the specified entry in the utmp file.  If    */
  699. /*    it can't find it, it returns NULL.                */
  700.  
  701. struct utmp *getutid(entry)
  702. register struct utmp *entry;
  703. {
  704.     extern struct utmp ubuf;
  705.     struct utmp *getutent();
  706.     register short type;
  707.  
  708. /* Start looking for entry.  Look in our current buffer before */
  709. /* reading in new entries. */
  710.     do {
  711.  
  712. /* If there is no entry in "ubuf", skip to the read. */
  713.         if (ubuf.ut_type != EMPTY) {
  714.             switch(entry->ut_type) {
  715.  
  716. /* Do not look for an entry if the user sent us an EMPTY entry. */
  717.             case EMPTY:
  718.                 return(NULL);
  719.  
  720. /* For RUN_LVL, BOOT_TIME, OLD_TIME, and NEW_TIME entries, only */
  721. /* the types have to match.  If they do, return the address of */
  722. /* internal buffer. */
  723.             case RUN_LVL:
  724.             case BOOT_TIME:
  725.             case OLD_TIME:
  726.             case NEW_TIME:
  727.                 if (entry->ut_type == ubuf.ut_type) return(&ubuf);
  728.                 break;
  729.  
  730. /* For INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS, and DEAD_PROCESS */
  731. /* the type of the entry in "ubuf", must be one of the above and */
  732. /* id's must match. */
  733.             case INIT_PROCESS:
  734.             case LOGIN_PROCESS:
  735.             case USER_PROCESS:
  736.             case DEAD_PROCESS:
  737.                 if (((type = ubuf.ut_type) == INIT_PROCESS
  738.                     || type == LOGIN_PROCESS
  739.                     || type == USER_PROCESS
  740.                     || type == DEAD_PROCESS)
  741.                     && ubuf.ut_id[0] == entry->ut_id[0]
  742.                     && ubuf.ut_id[1] == entry->ut_id[1]
  743.                     && ubuf.ut_id[2] == entry->ut_id[2]
  744.                     && ubuf.ut_id[3] == entry->ut_id[3])
  745.                     return(&ubuf);
  746.                 break;
  747.  
  748. /* Do not search for illegal types of entry. */
  749.             default:
  750.                 return(NULL);
  751.             }
  752.         }
  753.     } while (getutent() != NULL);
  754.  
  755. /* Return NULL since the proper entry wasn't found. */
  756.     return(NULL);
  757. }
  758.  
  759. /* "getutline" searches the "utmp" file for a LOGIN_PROCESS or
  760.  * USER_PROCESS with the same "line" as the specified "entry".
  761.  */
  762.  
  763. struct utmp *getutline(entry)
  764. register struct utmp *entry;
  765. {
  766.     extern struct utmp ubuf,*getutent();
  767.     register struct utmp *cur;
  768.  
  769. /* Start by using the entry currently incore.  This prevents */
  770. /* doing reads that aren't necessary. */
  771.     cur = &ubuf;
  772.     do {
  773. /* If the current entry is the one we are interested in, return */
  774. /* a pointer to it. */
  775.         if (cur->ut_type != EMPTY && (cur->ut_type == LOGIN_PROCESS
  776.             || cur->ut_type == USER_PROCESS) && strncmp(&entry->ut_line[0],
  777.             &cur->ut_line[0],sizeof(cur->ut_line)) == 0) return(cur);
  778.     } while ((cur = getutent()) != NULL);
  779.  
  780. /* Since entry wasn't found, return NULL. */
  781.     return(NULL);
  782. }
  783.  
  784. /*    "pututline" writes the structure sent into the utmp file.    */
  785. /*    If there is already an entry with the same id, then it is    */
  786. /*    overwritten, otherwise a new entry is made at the end of the    */
  787. /*    utmp file.                            */
  788.  
  789. struct utmp *pututline(entry)
  790. struct utmp *entry;
  791. {
  792.     int fc;
  793.     struct utmp *answer;
  794.     extern long time();
  795.     extern struct utmp ubuf;
  796.     extern long loc_utmp,lseek();
  797.     extern struct utmp *getutid();
  798.     extern int fd,errno;
  799.     struct utmp tmpbuf;
  800.  
  801. /* Copy the user supplied entry into our temporary buffer to */
  802. /* avoid the possibility that the user is actually passing us */
  803. /* the address of "ubuf". */
  804.     tmpbuf = *entry;
  805.     getutent();
  806.     if (fd < 0) {
  807. #ifdef    ERRDEBUG
  808.         gdebug("pututline: Unable to create utmp file.\n");
  809. #endif
  810.         return((struct utmp *)NULL);
  811.     }
  812. /* Make sure file is writable */
  813.     if ((fc=fcntl(fd, F_GETFL, NULL)) == -1
  814.         || (fc & O_RDWR) != O_RDWR) {
  815.         return((struct utmp *)NULL);
  816.     }
  817.  
  818. /* Find the proper entry in the utmp file.  Start at the current */
  819. /* location.  If it isn't found from here to the end of the */
  820. /* file, then reset to the beginning of the file and try again. */
  821. /* If it still isn't found, then write a new entry at the end of */
  822. /* the file.  (Making sure the location is an integral number of */
  823. /* utmp structures into the file incase the file is scribbled.) */
  824.  
  825.     if (getutid(&tmpbuf) == NULL) {
  826. #ifdef    ERRDEBUG
  827.         gdebug("First getutid() failed.  fd: %d",fd);
  828. #endif
  829.         setutent();
  830.         if (getutid(&tmpbuf) == NULL) {
  831. #ifdef    ERRDEBUG
  832.             loc_utmp = lseek(fd, 0L, 1);
  833.             gdebug("Second getutid() failed.  fd: %d loc_utmp: %ld\n",fd,loc_utmp);
  834. #endif
  835.             fcntl(fd, F_SETFL, fc | O_APPEND);
  836.         } else {
  837.             lseek(fd, -(long)sizeof(struct utmp), 1);
  838.         }
  839.     } else {
  840.         lseek(fd, -(long)sizeof(struct utmp), 1);
  841.     }
  842.  
  843. /* Write out the user supplied structure.  If the write fails, */
  844. /* then the user probably doesn't have permission to write the */
  845. /* utmp file. */
  846.     if (write(fd,&tmpbuf,sizeof(tmpbuf)) != sizeof(tmpbuf)) {
  847. #ifdef    ERRDEBUG
  848.         gdebug("pututline failed: write-%d\n",errno);
  849. #endif
  850.         answer = (struct utmp *)NULL;
  851.     } else {
  852. /* Copy the user structure into ubuf so that it will be up to */
  853. /* date in the future. */
  854.         ubuf = tmpbuf;
  855.         answer = &ubuf;
  856.  
  857. #ifdef    ERRDEBUG
  858.         gdebug("id: %c%c loc: %x\n",ubuf.ut_id[0],ubuf.ut_id[1],
  859.             ubuf.ut_id[2],ubuf.ut_id[3],loc_utmp);
  860. #endif
  861.     }
  862.     fcntl(fd, F_SETFL, fc);
  863.     return(answer);
  864. }
  865.  
  866. /*    "setutent" just resets the utmp file back to the beginning.    */
  867.  
  868. setutent()
  869. {
  870.     register char *ptr;
  871.     register int i;
  872.     extern int fd;
  873.     extern struct utmp ubuf;
  874.     extern long loc_utmp;
  875.  
  876.     if (fd != -1) lseek(fd,0L,0);
  877.  
  878. /* Zero the stored copy of the last entry read, since we are */
  879. /* resetting to the beginning of the file. */
  880.  
  881.     for (i=0,ptr=(char*)&ubuf; i < sizeof(ubuf);i++) *ptr++ = '\0';
  882.     loc_utmp = 0L;
  883. }
  884.  
  885. /*    "endutent" closes the utmp file.                */
  886.  
  887. endutent()
  888. {
  889.     extern int fd;
  890.     extern long loc_utmp;
  891.     extern struct utmp ubuf;
  892.     register char *ptr;
  893.     register int i;
  894.  
  895.     if (fd != -1) close(fd);
  896.     fd = -1;
  897.     loc_utmp = 0;
  898.     for (i=0,ptr= (char *)(&ubuf); i < sizeof(ubuf);i++) *ptr++ = '\0';
  899. }
  900.  
  901. /*    "utmpname" allows the user to read a file other than the    */
  902. /*    normal "utmp" file.                        */
  903.  
  904. utmpname(newfile)
  905. char *newfile;
  906. {
  907.     extern char utmpfile[];
  908.  
  909. /* Determine if the new filename will fit.  If not, return 0. */
  910.     if (strlen(newfile) > MAXFILE) return (0);
  911.  
  912. /* Otherwise copy in the new file name. */
  913.     else strcpy(&utmpfile[0],newfile);
  914.  
  915. /* Make sure everything is reset to the beginning state. */
  916.     endutent();
  917.     return(1);
  918. }
  919.  
  920. #ifdef    ERRDEBUG
  921. #include    <stdio.h>
  922.  
  923. gdebug(format,arg1,arg2,arg3,arg4,arg5,arg6)
  924. char *format;
  925. int arg1,arg2,arg3,arg4,arg5,arg6;
  926. {
  927.     register FILE *fp;
  928.     register int errnum;
  929.     extern int errno;
  930.  
  931.     if ((fp = fopen("/etc/dbg.getut","a+")) == NULL) return;
  932.     fprintf(fp,format,arg1,arg2,arg3,arg4,arg5,arg6);
  933.     fclose(fp);
  934. }
  935. #endif
  936.